import { GameWithMetadata } from "@/components/game-list";
import { InterfaceConfig_GameListEntryImageJson } from "@retrom/codegen/retrom/client/client-config_pb";
import { getFileStub } from "@/lib/utils";
import { useConfig } from "@/providers/config";
import { useNavigate } from "@tanstack/react-router";
import { useCallback, useMemo } from "react";
import { FocusContainer, useFocusable } from "../focus-container";
import { HotkeyLayer } from "@/providers/hotkeys/layers";
import { Group, useGroupContext } from "@/providers/fullscreen/group-context";
import { Separator } from "@retrom/ui/components/separator";
import { cn } from "@retrom/ui/lib/utils";
import { useGameMetadata } from "@/queries/useGameMetadata";
import { createUrl, usePublicUrl } from "@/utils/urls";
import { Skeleton } from "@retrom/ui/components/skeleton";

function getFirstGameId(group: Group) {
  const firstPartitionWithGames = group.partitionedGames.find(
    ([_, games]) => !!games.length,
  );

  return firstPartitionWithGames?.[1][0].id;
}

export function GridGameList() {
  const { activeGroup, allGroups } = useGroupContext();

  const { columns = 4, gap = 20 } =
    useConfig((s) => s.config?.interface?.fullscreenConfig?.gridList) ?? {};

  const getDelay = useCallback(
    (idx: number) => {
      const col = idx % columns;

      return col * 150;
    },
    [columns],
  );

  return allGroups.map((group) =>
    group.id === activeGroup?.id ? (
      <FocusContainer
        key={group.id}
        opts={{
          focusKey: `game-list-${group.id}`,
          saveLastFocusedChild: false,
        }}
        style={{ "--game-cols": columns, "--game-gap": `${gap}px` }}
        className={cn("flex flex-col gap-4 w-full mx-auto py-[20dvh] px-4")}
      >
        {group.allGames.length === 0 && (
          <div className="flex flex-col gap-4 items-center justify-center">
            <h2 className="text-foreground/80 font-black text-2xl">
              No games found 😔
            </h2>
            <p className="text-foreground/50">
              Please add some games to your library.
            </p>
          </div>
        )}
        {activeGroup?.partitionedGames
          ?.filter(([_, games]) => !!games.length)
          .map(([key, games]) => (
            <FocusContainer
              opts={{
                focusKey: `game-list-${activeGroup.id}-${key}-container`,
                focusable: !!games.length,
                saveLastFocusedChild: false,
              }}
              key={key}
              className={cn(!games.length ? "hidden" : "block")}
            >
              <div
                className={cn(
                  "grid gap-4 place-items-center mb-4 px-4",
                  "grid-cols-[1fr,auto,1fr]",
                )}
              >
                <Separator className="bg-foreground/30" />

                <h3
                  id={`game-list-header-${key}`}
                  className="uppercase font-black text-xl text-foreground/80 scroll-mt-16"
                >
                  {key}
                </h3>

                <Separator className="bg-foreground/30" />
              </div>

              <div
                className={cn(
                  "grid w-full gap-[var(--game-gap)]",
                  "grid-cols-[repeat(var(--game-cols),minmax(0,1fr))]",
                )}
              >
                {games.map((game) => (
                  <div
                    key={game.id}
                    style={{
                      animationDelay: `${getDelay(group.allGames.findIndex(({ id }) => id === game.id))}ms`,
                    }}
                    className={cn(
                      "animate-in fade-in fill-mode-both duration-500",
                    )}
                  >
                    <GameListItem
                      game={game}
                      id={`game-list-${group.id}-${game.id}`}
                      initialFocus={game.id === getFirstGameId(group)}
                    />
                  </div>
                ))}
              </div>
            </FocusContainer>
          ))}
      </FocusContainer>
    ) : null,
  );
}

function GameListItem(props: {
  game: GameWithMetadata;
  id: string;
  initialFocus?: boolean;
}) {
  const { game, id, initialFocus } = props;

  const navigate = useNavigate();
  const { ref } = useFocusable<HTMLDivElement>({
    focusKey: id,
    forceFocus: true,
    initialFocus,
    onFocus: ({ node }) => {
      node?.scrollIntoView({
        behavior: "smooth",
        block: "center",
        inline: "center",
      });
    },
  });

  const { imageType = "COVER" } =
    useConfig((s) => s.config?.interface?.fullscreenConfig?.gridList) ?? {};

  return (
    <div
      className={cn(
        "group scale-95 focus-within:scale-100 hover:scale-100 transition-all",
        "shadow-lg shadow-background relative cursor-pointer",
        "rounded h-full w-full",
      )}
    >
      <HotkeyLayer
        handlers={{ ACCEPT: { handler: () => ref.current?.click() } }}
      >
        <div
          tabIndex={-1}
          id={id}
          ref={ref}
          className={cn("border-none outline-none")}
          onClick={() =>
            void navigate({
              to: "/fullscreen/games/$gameId",
              params: { gameId: game.id.toString() },
            })
          }
        >
          <GameImage game={game} kind={imageType} />
        </div>
      </HotkeyLayer>
    </div>
  );
}

function GameImage(props: {
  game: GameWithMetadata;
  kind: InterfaceConfig_GameListEntryImageJson;
}) {
  const { game } = props;
  const publicUrl = usePublicUrl();

  const { data, status } = useGameMetadata({
    request: { gameIds: [game.id] },
    selectFn: (data) => ({
      metadata: data.metadata.at(0),
      mediaPaths:
        game.id in data.mediaPaths ? data.mediaPaths[game.id] : undefined,
    }),
  });

  const coverUrl = useMemo(() => {
    const localPath = data?.mediaPaths?.coverUrl;
    if (localPath && publicUrl) {
      return createUrl({ path: localPath, base: publicUrl })?.href;
    }

    return data?.metadata?.coverUrl;
  }, [publicUrl, data]);

  const backgroundUrl = useMemo(() => {
    const localPath = data?.mediaPaths?.backgroundUrl;
    if (localPath && publicUrl) {
      return createUrl({ path: localPath, base: publicUrl })?.href;
    }

    return data?.metadata?.backgroundUrl;
  }, [publicUrl, data]);

  const imageSrc = props.kind === "BACKGROUND" ? backgroundUrl : coverUrl;
  const gameName = game.metadata?.name ?? getFileStub(game.path);

  return (
    <div
      key={game.id}
      className={cn(
        props.kind === "BACKGROUND" ? "aspect-video" : "aspect-[3/4]",
        "rounded overflow-hidden relative",
        "h-fit w-fit min-w-full min-h-full",
      )}
    >
      {status === "pending" ? (
        <Skeleton className="size-full" />
      ) : imageSrc ? (
        <img
          loading="lazy"
          src={imageSrc}
          className={cn("absolute object-cover min-w-full min-h-full")}
        />
      ) : null}

      <div
        className={cn(
          "group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0",
          "absolute inset-0",
          "bg-gradient-to-t from-card",
          "ring-ring ring-inset group-focus-within:ring-4",
          props.kind === "BACKGROUND" ? "text-lg py-2 px-4" : "text-2xl p-4",
          "flex items-end font-black",
        )}
      >
        <p className="text-pretty">{gameName}</p>
      </div>
    </div>
  );
}
